﻿#region << 版 本 注 释 >>
/*----------------------------------------------------------------
 * 版权所有 (c) 2022 北京超维景生物科技有限公司 保留所有权利。
 * 
 * 创建者：huangyang
 * 电子邮箱：huangyang@tvscope.cn
 * 创建时间：2023/2/21 17:23:09
 * 版本：V1.0.0
 * 描述：
 *
 * ----------------------------------------------------------------
 * 修改人：
 * 时间：
 * 修改说明：
 *
 * 版本：V1.0.1
 *----------------------------------------------------------------*/
#endregion << 版 本 注 释 >>

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ImageK.Plugin;

namespace ImageK.Process
{
    public class StackProcessor
    {
        public const int FILTER_MEAN = Filters3D.MEAN, FILTER_MEDIAN = Filters3D.MEDIAN, FILTER_MIN = Filters3D.MIN,
            FILTER_MAX = Filters3D.MAX, FILTER_VAR = Filters3D.VAR, FILTER_MAXLOCAL = Filters3D.MAXLOCAL;

        private ImageStack stack;
        private ImageProcessor ip;
        int nSlices;
        double xScale, yScale;
        int[] table;
        double fillValue;
        float[] voxels;

        /** Constructs a StackProcessor from a stack. */
        public StackProcessor(ImageStack stack): this(stack, null)
        {
            
        }

        /** Constructs a StackProcessor from a stack. 'ip' is the
            processor that will be used to process the slices. 
            'ip' can be null when using crop(). */
        public StackProcessor(ImageStack stack, ImageProcessor ip)
        {
            this.stack = stack;
            this.ip = ip;
            nSlices = stack.size();
            if (nSlices > 1 && ip != null)
                ip.setProgressBar(null);
        }

        const int FLIPH = 0, FLIPV = 1, SCALE = 2, INVERT = 3, APPLY_TABLE = 4, SCALE_WITH_FILL = 5;

        void process(int command)
        {
            string s = "";
            ImageProcessor ip2 = stack.getProcessor(1);
            switch (command)
            {
                case FLIPH: case FLIPV: s = "Flip: "; break;
                case SCALE: s = "Scale: "; break;
                case SCALE_WITH_FILL: s = "Scale: "; ip2.setBackgroundValue(fillValue); break;
                case INVERT: s = "Invert: "; break;
                case APPLY_TABLE: s = "Apply: "; break;
            }
            if (ip == null)
                ip = ip2;
            ip2.setRoi(this.ip.getRoi());
            ip2.setInterpolate(this.ip.getInterpolate());
            for (int i = 1; i <= nSlices; i++)
            {
                showStatus(s, i, nSlices);
                ip2.setPixels(stack.getPixels(i));
                if (nSlices == 1 && i == 1 && command == SCALE)
                    ip2.snapshot();
                switch (command)
                {
                    case FLIPH: ip2.flipHorizontal(); break;
                    case FLIPV: ip2.flipVertical(); break;
                    case SCALE: case SCALE_WITH_FILL: ip2.scale(xScale, yScale); break;
                    case INVERT: ip2.invert(); break;
                    case APPLY_TABLE: ip2.applyTable(table); break;
                }
                IJ.showProgress((double)i / nSlices);
            }
            IJ.showProgress(1.0);
        }



        public void flipVertical()
        {
            process(FLIPV);
        }

        public void applyTable(int[] table)
        {
            this.table = table;
            process(APPLY_TABLE);
        }

        public void scale(double xScale, double yScale)
        {
            this.xScale = xScale;
            this.yScale = yScale;
            process(SCALE);
        }

        public void scale(double xScale, double yScale, double fillValue)
        {
            this.xScale = xScale;
            this.yScale = yScale;
            this.fillValue = fillValue;
            process(SCALE_WITH_FILL);
        }

        /** Creates a new stack with dimensions 'newWidth' x 'newHeight'.
        To reduce memory requirements, the orginal stack is deleted
        as the new stack is created. */
        public ImageStack resize(int newWidth, int newHeight)
        {
            return resize(newWidth, newHeight, false);
        }

        public ImageStack resize(int newWidth, int newHeight, bool averageWhenDownsizing)
        {
            ImageStack stack2 = new ImageStack(newWidth, newHeight);
            ImageProcessor ip2;
            Rectangle roi = ip != null ? ip.getRoi() : Rectangle.Empty;
            if (ip == null)
                ip = stack.getProcessor(1).duplicate();
            try
            {
                for (int i = 1; i <= nSlices; i++)
                {
                    showStatus("Resize: ", i, nSlices);
                    ip.setPixels(stack.getPixels(1));
                    String label = stack.getSliceLabel(1);
                    stack.deleteSlice(1);
                    ip2 = ip.resize(newWidth, newHeight, averageWhenDownsizing);
                    if (ip2 != null)
                        stack2.addSlice(label, ip2);
                    IJ.showProgress((double)i / nSlices);
                }
                IJ.showProgress(1.0);
            }
            catch (OutOfMemoryException o)
            {
                while (stack.size() > 1)
                    stack.deleteLastSlice();
                IJ.outOfMemory("StackProcessor.resize");
                IJ.showProgress(1.0);
            }
            return stack2;
        }

        void showStatus(String s, int n, int total)
        {
            IJ.ShowStatus(s + n + "/" + total);
        }
    }
}
